using System;
using System.Globalization;

namespace zijian666.AnyExtensions;

/// <summary>
/// 数字类型的拓展方法
/// </summary>
public static class NumberExtensions
{
    /// <summary>
    /// 将 double 转为 decimal
    /// </summary>
    public static decimal ToDecimal(this double value)
        => decimal.Parse(value.ToString("G15"), NumberStyles.Any); // G17 在 1.005d 时会变成 1.00499999... G15暂时没有发现问题

    /// <summary>
    /// 将 float 转为 decimal
    /// </summary>
    public static decimal ToDecimal(this float value)
        => decimal.Parse(value.ToString("G9"), NumberStyles.Any);

    /// <summary>
    /// 将小数值舍入到指定精度。 一个参数，指定当一个值正好处于另两个数中间时如何舍入这个值，默认：四舍五入。
    /// </summary>
    public static decimal Round(this decimal value, byte decimals, MidpointRounding mode = MidpointRounding.AwayFromZero)
        => decimal.Round(value, decimals, mode);
    /// <summary>
    /// 将小数值舍入到指定精度。 一个参数，指定当一个值正好处于另两个数中间时如何舍入这个值，默认：四舍五入。。
    /// </summary>
    public static double Round(this double value, byte decimals, MidpointRounding mode = MidpointRounding.AwayFromZero)
        => (double)decimal.Round(value.ToDecimal(), decimals, mode);
    /// <summary>
    /// 将小数值舍入到指定精度。 一个参数，指定当一个值正好处于另两个数中间时如何舍入这个值，默认：四舍五入。。
    /// </summary>
    public static float Round(this float value, byte decimals, MidpointRounding mode = MidpointRounding.AwayFromZero)
        => (float)decimal.Round(value.ToDecimal(), decimals, mode);

    /// <summary>
    /// 将小数值舍入到指定精度。 一个参数，指定当一个值正好处于另两个数中间时如何舍入这个值，默认：四舍五入。
    /// </summary>
    public static decimal Round(this decimal value, MidpointRounding mode = MidpointRounding.AwayFromZero)
        => decimal.Round(value, mode);
    /// <summary>
    /// 将小数值舍入到指定精度。 一个参数，指定当一个值正好处于另两个数中间时如何舍入这个值，默认：四舍五入。。
    /// </summary>
    public static double Round(this double value, MidpointRounding mode = MidpointRounding.AwayFromZero)
        => (double)decimal.Round(value.ToDecimal(), mode);
    /// <summary>
    /// 将小数值舍入到指定精度。 一个参数，指定当一个值正好处于另两个数中间时如何舍入这个值，默认：四舍五入。。
    /// </summary>
    public static float Round(this float value, MidpointRounding mode = MidpointRounding.AwayFromZero)
        => (float)decimal.Round(value.ToDecimal(), mode);

    /// <summary>
    /// 返回大于或等于指定小数位的数
    /// </summary>
    public static decimal Ceiling(this decimal value, byte decimals = 0)
    {
        if (value == 0)
        {
            return value;
        }
        if (decimals == 0)
        {
            return decimal.Ceiling(value);
        }
        var pow = (decimal)Math.Pow(10, decimals);
        return decimal.Ceiling(value * pow) / pow;
    }
    /// <summary>
    /// 返回大于或等于指定小数位的数
    /// </summary>
    public static double Ceiling(this double value, byte decimals = 0)
        => (double)Ceiling(value.ToDecimal(), decimals);
    /// <summary>
    /// 返回大于或等于指定小数位的数
    /// </summary>
    public static float Ceiling(this float value, byte decimals = 0)
        => (float)Ceiling(value.ToDecimal(), decimals);

    /// <summary>
    /// 返回小于或等于指定小数位的数
    /// </summary>
    public static decimal Floor(this decimal value, byte decimals = 0)
    {
        if (value == 0)
        {
            return value;
        }
        if (decimals == 0)
        {
            return decimal.Floor(value);
        }
        var pow = (decimal)Math.Pow(10, decimals);
        return decimal.Floor(value * pow) / pow;
    }
    /// <summary>
    /// 返回小于或等于指定小数位的数
    /// </summary>
    public static double Floor(this double value, byte decimals = 0)
        => (double)Floor(value.ToDecimal(), decimals);

    /// <summary>
    /// 返回小于或等于指定小数位的数
    /// </summary>
    public static float Floor(this float value, byte decimals = 0)
        => (float)Floor(value.ToDecimal(), decimals);

    /// <summary>
    /// 按指定模式和位数对数字进行处理
    /// </summary>
    public static decimal Scale(this decimal value, byte decimals, NumberScaleMode mode)
    {
        return mode switch
        {
            NumberScaleMode.Round => value.Round(decimals),
            NumberScaleMode.Ceiling => value.Ceiling(decimals),
            _ => value.Floor(decimals),
        };
    }

    /// <summary>
    /// 按指定模式和位数对数字进行处理
    /// </summary>
    public static double Scale(this double value, byte decimals, NumberScaleMode mode)
        => (double)Scale(value.ToDecimal(), decimals, mode);

    /// <summary>
    /// 按指定模式和位数对数字进行处理
    /// </summary>
    public static float Scale(this float value, byte decimals, NumberScaleMode mode)
        => (float)Scale(value.ToDecimal(), decimals, mode);

    /// <summary>
    /// 按指定模式和位数对数字进行处理
    /// </summary>
    public static decimal Scale(this decimal value, NumberScaleMode mode)
        => Scale(value, 0, mode);

    /// <summary>
    /// 按指定模式和位数对数字进行处理
    /// </summary>
    public static double Scale(this double value, NumberScaleMode mode)
        => (double)Scale(value.ToDecimal(), 0, mode);

    /// <summary>
    /// 按指定模式和位数对数字进行处理
    /// </summary>
    public static float Scale(this float value, NumberScaleMode mode)
        => (float)Scale(value.ToDecimal(), 0, mode);
}


/// <summary>
/// 数字剪切模式
/// </summary>
public enum NumberScaleMode
{
    /// <summary>
    /// 四舍五入
    /// </summary>
    Round,
    /// <summary>
    /// 向上取整
    /// </summary>
    Ceiling,
    /// <summary>
    /// 向下取整
    /// </summary>
    Floor
}